package com.wcs.exam.common.llm;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.wcs.exam.common.base.exception.BaseException;
import com.wcs.exam.common.enums.LlmPlatformEnum;
import com.wcs.exam.common.llm.bean.AiQuestion;
import com.wcs.exam.common.llm.bean.RoleEnum;
import com.wcs.exam.common.llm.impl.bean.Message;
import com.wcs.exam.common.llm.impl.bean.ModelBean;
import com.wcs.exam.common.llm.req.AiQuestionGradingReq;
import com.wcs.exam.common.llm.resp.AiQuestionGradingResp;
import com.wcs.exam.common.util.BeanUtil;
import com.wcs.exam.common.util.JsonUtil;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 大模型
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class LlmService {

    @NotNull
    private final Map<String, LlmFace> modelFaceMap;

    /**
     * AI出题
     *
     * @param llm        大模型参数
     * @param aiQuestion AI出题
     */
    public SseEmitter questionAiCompletions(Llm llm, AiQuestion aiQuestion) {
        // 组装内容
        List<Message> messageList = new ArrayList<>();
        String systemPrompt = llm.getLlmQuestionSystemPrompt();
        if (!StringUtils.hasText(systemPrompt)) {
            systemPrompt = "你是一个出题专家，请根据用户的要求，生成对应的试题信息。必须按照以下格式返回：【{题目类型}】{序号}.{题干}\n{选项内容}\n答案：{答案选项}\n难度：{简单|一般|困难}\n解析：{解析内容}";
        }
        messageList.add(new Message(RoleEnum.system.name(), systemPrompt));

        String userPrompt = llm.getLlmQuestionUserPrompt();
        if (!StringUtils.hasText(systemPrompt)) {
            userPrompt = "基于以下内容：【{question_content}】，出{danx_count}道单选题，{duox_count}道多选题，{pand_count}道判断题，{tiank_count}道填空题，{jiand_count}道简答题。";
        }
        userPrompt = userPrompt
                .replace("{question_content}", aiQuestion.getQuestionContent())
                .replace("{danx_count}", aiQuestion.getDanxCount().toString())
                .replace("{duox_count}", aiQuestion.getDuoxCount().toString())
                .replace("{pand_count}", aiQuestion.getPandCount().toString())
                .replace("{tiank_count}", aiQuestion.getTiankCount().toString())
                .replace("{jiand_count}", aiQuestion.getJiandCount().toString());
        messageList.add(new Message(RoleEnum.user.name(), userPrompt));

        ModelBean modelBean;
        LlmPlatformEnum llmPlatformEnum = LlmPlatformEnum.byCode(llm.getLlmPlatform());
        if (ObjectUtil.isNull(llmPlatformEnum)) {
            throw new BaseException("大模型不存在");
        }
        modelBean = switch (llmPlatformEnum) {
            case DEEPSEEK -> ModelBean.builder()
                    .apiKey(llm.getDeepSeekApiKey())
                    .model(llm.getDeepSeekModel())
                    .build();
            case ZHIPU -> ModelBean.builder()
                    .apiKey(llm.getZhiPuApiKey())
                    .model(llm.getZhiPuModel())
                    .build();
        };
        return chat(llmPlatformEnum.getMode(), modelBean, messageList);
    }

    /**
     * AI试题评分
     *
     * @param llm 大模型参数
     * @param req AI评分参数
     * @return 试题评分结果
     */
    public AiQuestionGradingResp questionGrading(Llm llm, AiQuestionGradingReq req) {
        // 组装内容
        List<Message> messageList = new ArrayList<>();
        String systemPrompt = llm.getLlmQuestionGradingSystemPrompt();
        if (StrUtil.isBlank(systemPrompt)) {
            systemPrompt = """
                    你是一位遵循教育测量标准的资深阅卷专家，评分时需严格遵循以下规则：
                    1.填空题根据参考答案判断存在多少个填空，且题目每个填空的分数一样，填空题的答案以“|”进行分隔。
                    2.先分析题目信息判断对参考答案的顺序是否有要求，再判断用户答案的顺序，顺序错误扣分。
                    3.先判断用户答案是否符合题目核心要求，再逐步核对学生解答，错误步骤或逻辑断层扣分。
                    4.输出格式：{userScore:$用户得分,reason:$评分依据}。
                    5.语言风格：客观严谨，避免模糊表述，使用学科专业术语。
                    """;
        }
        messageList.add(new Message(RoleEnum.system.name(), systemPrompt));

        String userPrompt = llm.getLlmQuestionGradingUserPrompt();
        if (StrUtil.isBlank(userPrompt)) {
            userPrompt = """
                    请根据题目信息给用户答案进行评分：
                    题目信息：{questionTitle}
                    题目分值：{questionScore}分
                    参考答案：{questionAnswer}
                    用户答案：{userAnswer}
                    """;
        }
        String targetUserPrompt = userPrompt
                .replace("{questionTitle}", req.getQuestionTitle())
                .replace("{questionScore}", NumberUtil.decimalFormat("0.00", req.getQuestionScore()))
                .replace("{questionAnswer}", req.getQuestionAnswer())
                .replace("{userAnswer}", req.getUserAnswer());
        messageList.add(new Message(RoleEnum.user.name(), targetUserPrompt));

        ModelBean modelBean;
        LlmPlatformEnum llmPlatformEnum = LlmPlatformEnum.byCode(llm.getLlmPlatform());
        if (ObjectUtil.isNull(llmPlatformEnum)) {
            throw new BaseException("大模型不存在");
        }
        modelBean = switch (llmPlatformEnum) {
            case DEEPSEEK -> ModelBean.builder()
                    .apiKey(llm.getDeepSeekApiKey())
                    .model(llm.getDeepSeekModel())
                    .build();
            case ZHIPU -> ModelBean.builder()
                    .apiKey(llm.getZhiPuApiKey())
                    .model(llm.getZhiPuModel())
                    .build();
        };
        LlmFace face = modelFaceMap.get(llmPlatformEnum.getMode());
        LlmBaseResp baseResp = face.http(modelBean, messageList);
        log.info("llm-grading-result: {}", JsonUtil.toJsonString(baseResp));

        if (ObjectUtil.isNull(baseResp)) {
            return null;
        }
        JSONObject contentJson = JSONUtil.parseObj(baseResp.getOutput());

        AiQuestionGradingResp resp = new AiQuestionGradingResp();
        resp.setUserScore(contentJson.getBigDecimal("userScore"));
        resp.setReason(contentJson.getStr("reason"));
        resp.setUsage(BeanUtil.copyProperties(baseResp.getUsage(), AiQuestionGradingResp.Usage.class));
        return resp;
    }

    public SseEmitter chat(String modelType, ModelBean modelBean, List<Message> messages) {
        LlmFace modelFace = modelFaceMap.get(modelType);
        if (modelFace == null) {
            throw new RuntimeException("暂不支持该大模型");
        }
        return modelFace.chat(modelFace, modelBean, messages);
    }
}
